#![cfg_attr(not(feature = "std"), no_std)]
#![warn(missing_debug_implementations)]
#![doc(html_logo_url = "https://raw.githubusercontent.com/georust/meta/master/logo/logo.png")]
//! geo-types 库定义了[GeoRust]生态系统中支持的几何类型
//!
//! 在大多数情况下，只有当我们是需要开发一个GIS有关的crate，并且想要让我们的crate能够与Rust的GeoRust的其他crate想兼容的时候，
//! 才需要独立的使用到这个库，否则，直接使用[`geo`](https://crates.io/crates/geo)是更好的选择，
//! 因为geo的crate 直接封装了这些地理类型，并额外提供地理空间算法。
//! 
//!
//! ## 几何数据结构
//!
//! - **[`Point`]**:一个由 [`Coord`] 组成的简单点结构
//! - **[`MultiPoint`]**:一个 [`Point`]的集合
//! - **[`Line`]**: 由两组 [`Coord`]组合的简单线段
//! - **[`LineString`]**: 一组连续的线段，由两组或者多组[`Coord`]所组成
//! - **[`MultiLineString`]**: 一个 [`LineString`]的集合
//! - **[`Polygon`]**: 一个由 [`LineString`] 形成的外环和0个或多个 [`LineString`] 形成的内环所表达是有界区域。
//! - **[`MultiPolygon`]**: 一个 [`Polygon`]的集合
//! - **[`Rect`]**: 一个轴对齐的有界区域，由两组[`Coord`]所表示，分别是最大值和最小值。
//! - **[`Triangle`]**:  由三个[`Coord`]顶点表示的有界区域（即三角形）
//! - **[`GeometryCollection`]**: 一组[`Geometry`]集合
//! - **[`Geometry`]**: 除去[`Coord`]以外的，上述所以的几何类型，是几何枚举类型的基类
//!
//! ## 坐标和数值类型
//! 
//! - **[`Coord`]**: 一组二维坐标。虽然所有的几何类型都是由 [`Coord`]组成，但是 [`Coord`]本身确不是几何类型（也就说，地理空间的哪些算法，在 [`Coord`]上面
//! 是不可用的），具体可以参阅 [`Point`] 了解单个坐标几何图形。
//! 
//! 默认情况下，坐标值的类型是是 64 位浮点数，但这是其结构是泛型的，所以也可以指定实现 [`CoordNum`] 或 [`CoordFloat`] 的任何数值类型。 
//! 除了 [`f64`] 之外，还可以包括常见的数字类型，例如 [`f32`]、[`i32`]、[`i64`] 等。
//!
//! ```rust
//! use geo_types::Point;
//!
//! // 定义默认的f64类型的一个点要素
//! //std::mem::size_of可以用于计算该对象在内存中占用的空间（单位是字节，1个字节 = 8位）
//! let point: Point = Point::new(1.0, 2.0);
//! assert_eq!(std::mem::size_of::<Point>(), 64 * 2 / 8);
//!
//! // 可以在定义的实时，明确声明需要使用的数据类型
//! let f64_point: Point<f64> = Point::new(1.0, 2.0);
//! assert_eq!(std::mem::size_of::<Point<f64>>(), 64 * 2 / 8);
//!
//! // 或者指定一些非默认的数字类型，例如下面我们指定数据类型为f32
//! let f32_point: Point<f32> = Point::new(1.0, 2.0);
//! assert_eq!(std::mem::size_of::<Point<f32>>(), 32 * 2 / 8);
//!
//! // 定义为整数的也是可以的，但是很多算法无法兼容自定义的类型。
//! // 因为默认算法大多数是以浮点数来进行实现的。
//! let i32_point: Point<i32> = Point::new(1, 2);
//! assert_eq!(std::mem::size_of::<Point<i32>>(), 32 * 2 / 8);
//! ```
//!
//! # Semantics
//!
//! 此处提供的地理空间类型旨在遵守[OpenGIS Simple feature access][OGC-SFA]
//!标准。因此，这里的类型与标准的其他实现，例如[JTS]、[GEOS]是可相互操作的
//!
//! # Features
//!
//! 以下为可选[crate feature]：
//!
//!- `std`：允许使用完整的“std”库。默认情况下已启用。
//!- `approx`：允许检查几何图形类型是否与[approx]近似相等
//!- `arbitrary`：允许使用[arbitrary]从非结构化输入创建几何图形类型
//!- `serde`：允许使用[Sede]对几何体类型进行序列化和反序列化
//!- `use-rstar_0_8`：允许将几何图形类型插入[rstar]R*-树（`rstar v0.8`）
//!- `use-rstar_0_9`：允许将几何图形类型插入[rstar]R*-树（`rstar v0.9`）
//!- `use-rstar_0_10`：允许将几何图形类型插入[rstar]R*-树（`rstar v0.10`）
//!- `use-rstar_0_11`：允许将几何图形类型插入[rstar]R*-树（`rstar v0.11`）
//!
//!此库可用于`#![no_std]`环境来禁用了默认的`std`功能。
//!目前，`approx`和`use-rstar0_8`功能需要`std`。这更新可能会在未来发布。
//! 
//!
//! [approx]: https://github.com/brendanzab/approx
//! [arbitrary]: https://github.com/rust-fuzz/arbitrary
//! [Cargo features]: https://doc.rust-lang.org/cargo/reference/features.html
//! [GeoRust]: https://georust.org
//! [GEOS]: https://trac.osgeo.org/geos
//! [JTS]: https://github.com/locationtech/jts
//! [OGC-SFA]: https://www.ogc.org/standards/sfa
//! [rstar]: https://github.com/Stoeoef/rstar
//! [Serde]: https://serde.rs/
extern crate alloc;

use core::fmt::Debug;
use num_traits::{Float, Num, NumCast};

#[cfg(feature = "serde")]
#[macro_use]
extern crate serde;

#[cfg(test)]
#[macro_use]
extern crate approx;

#[deprecated(since = "0.7.0", note = "use `CoordFloat` or `CoordNum` instead")]
pub trait CoordinateType: Num + Copy + NumCast + PartialOrd + Debug {}
#[allow(deprecated)]
impl<T: Num + Copy + NumCast + PartialOrd + Debug> CoordinateType for T {}

///对于一些既可以使用整数由可以使用浮点的`point` /`Coord`的算法，针对浮点（“f32”和“f64”）和整数（“u8”、“i32”等）都进行了实现。
///而一些对于仅对浮点（如面积或长度计算）有意义的算法，请查阅 [CoordFloat](trait.CoordFloat.html).
#[allow(deprecated)]
pub trait CoordNum: CoordinateType + Debug {}
#[allow(deprecated)]
impl<T: CoordinateType + Debug> CoordNum for T {}

/// 此实现对于只能使用浮点`point`/ `Coord`的算法，如面积或长度计算
pub trait CoordFloat: CoordNum + Float {}
impl<T: CoordNum + Float> CoordFloat for T {}

pub mod geometry;
pub use geometry::*;

pub use geometry::line_string::PointsIter;

#[allow(deprecated)]
pub use geometry::rect::InvalidRectCoordinatesError;

mod error;
pub use error::Error;

#[macro_use]
mod macros;

#[macro_use]
mod wkt_macro;

#[cfg(feature = "arbitrary")]
mod arbitrary;

#[cfg(any(
    feature = "rstar_0_8",
    feature = "rstar_0_9",
    feature = "rstar_0_10",
    feature = "rstar_0_11"
))]
#[doc(hidden)]
pub mod private_utils;

#[doc(hidden)]
pub mod _alloc {
    //! 当std功能被禁用并且调用上下文缺少“extern crate alloc”时，需要从宏中的“alloc”访问这些类型。
    //! 此功能非public use。
    
    pub use ::alloc::vec;
}

#[cfg(test)]
mod tests {
    use alloc::vec;

    use super::*;
    use core::convert::TryFrom;

    #[test]
    fn type_test() {
        let c = coord! {
            x: 40.02f64,
            y: 116.34,
        };

        let p = Point::from(c);

        let Point(c2) = p;
        assert_eq!(c, c2);
        assert_relative_eq!(c.x, c2.x);
        assert_relative_eq!(c.y, c2.y);

        let p: Point<f32> = (0f32, 1f32).into();
        assert_relative_eq!(p.x(), 0.);
        assert_relative_eq!(p.y(), 1.);
    }

    #[test]
    fn convert_types() {
        let p: Point<f32> = Point::new(0., 0.);
        let p1 = p;
        let g: Geometry<f32> = p.into();
        let p2 = Point::try_from(g).unwrap();
        assert_eq!(p1, p2);
    }

    #[test]
    fn polygon_new_test() {
        let exterior = LineString::new(vec![
            coord! { x: 0., y: 0. },
            coord! { x: 1., y: 1. },
            coord! { x: 1., y: 0. },
            coord! { x: 0., y: 0. },
        ]);
        let interiors = vec![LineString::new(vec![
            coord! { x: 0.1, y: 0.1 },
            coord! { x: 0.9, y: 0.9 },
            coord! { x: 0.9, y: 0.1 },
            coord! { x: 0.1, y: 0.1 },
        ])];
        let p = Polygon::new(exterior.clone(), interiors.clone());

        assert_eq!(p.exterior(), &exterior);
        assert_eq!(p.interiors(), &interiors[..]);
    }

    #[test]
    fn iters() {
        let _: MultiPoint<_> = vec![(0., 0.), (1., 2.)].into();
        let _: MultiPoint<_> = vec![(0., 0.), (1., 2.)].into_iter().collect();

        let mut l1: LineString<_> = vec![(0., 0.), (1., 2.)].into();
        assert_eq!(l1[1], coord! { x: 1., y: 2. }); // index into linestring
        let _: LineString<_> = vec![(0., 0.), (1., 2.)].into_iter().collect();

        // index mutably into a linestring
        l1[0] = coord! { x: 1., y: 1. };
        assert_eq!(l1, vec![(1., 1.), (1., 2.)].into());
    }

    #[test]
    fn test_coordinate_types() {
        let p: Point<u8> = Point::new(0, 0);
        assert_eq!(p.x(), 0u8);

        let p: Point<i64> = Point::new(1_000_000, 0);
        assert_eq!(p.x(), 1_000_000i64);
    }

    #[cfg(feature = "rstar_0_8")]
    #[test]
    /// ensure Line's SpatialObject impl is correct
    fn line_test() {
        use rstar_0_8::primitives::Line as RStarLine;
        use rstar_0_8::{PointDistance, RTreeObject};

        let rl = RStarLine::new(Point::new(0.0, 0.0), Point::new(5.0, 5.0));
        let l = Line::new(coord! { x: 0.0, y: 0.0 }, coord! { x: 5., y: 5. });
        assert_eq!(rl.envelope(), l.envelope());
        // difference in 15th decimal place
        assert_relative_eq!(26.0, rl.distance_2(&Point::new(4.0, 10.0)));
        assert_relative_eq!(25.999999999999996, l.distance_2(&Point::new(4.0, 10.0)));
    }

    #[cfg(feature = "rstar_0_9")]
    #[test]
    /// ensure Line's SpatialObject impl is correct
    fn line_test_0_9() {
        use rstar_0_9::primitives::Line as RStarLine;
        use rstar_0_9::{PointDistance, RTreeObject};

        let rl = RStarLine::new(Point::new(0.0, 0.0), Point::new(5.0, 5.0));
        let l = Line::new(coord! { x: 0.0, y: 0.0 }, coord! { x: 5., y: 5. });
        assert_eq!(rl.envelope(), l.envelope());
        // difference in 15th decimal place
        assert_relative_eq!(26.0, rl.distance_2(&Point::new(4.0, 10.0)));
        assert_relative_eq!(25.999999999999996, l.distance_2(&Point::new(4.0, 10.0)));
    }

    #[cfg(feature = "rstar_0_10")]
    #[test]
    /// ensure Line's SpatialObject impl is correct
    fn line_test_0_10() {
        use rstar_0_10::primitives::Line as RStarLine;
        use rstar_0_10::{PointDistance, RTreeObject};

        let rl = RStarLine::new(Point::new(0.0, 0.0), Point::new(5.0, 5.0));
        let l = Line::new(coord! { x: 0.0, y: 0.0 }, coord! { x: 5., y: 5. });
        assert_eq!(rl.envelope(), l.envelope());
        // difference in 15th decimal place
        assert_relative_eq!(26.0, rl.distance_2(&Point::new(4.0, 10.0)));
        assert_relative_eq!(25.999999999999996, l.distance_2(&Point::new(4.0, 10.0)));
    }

    #[cfg(feature = "rstar_0_11")]
    #[test]
    /// ensure Line's SpatialObject impl is correct
    fn line_test_0_11() {
        use rstar_0_11::primitives::Line as RStarLine;
        use rstar_0_11::{PointDistance, RTreeObject};

        let rl = RStarLine::new(Point::new(0.0, 0.0), Point::new(5.0, 5.0));
        let l = Line::new(coord! { x: 0.0, y: 0.0 }, coord! { x: 5., y: 5. });
        assert_eq!(rl.envelope(), l.envelope());
        // difference in 15th decimal place
        assert_relative_eq!(26.0, rl.distance_2(&Point::new(4.0, 10.0)));
        assert_relative_eq!(25.999999999999996, l.distance_2(&Point::new(4.0, 10.0)));
    }

    #[test]
    fn test_rects() {
        let r = Rect::new(coord! { x: -1., y: -1. }, coord! { x: 1., y: 1. });
        let p: Polygon<_> = r.into();
        assert_eq!(
            p,
            Polygon::new(
                vec![(-1., -1.), (1., -1.), (1., 1.), (-1., 1.), (-1., -1.)].into(),
                vec![]
            )
        );
    }
}
